/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.base.util;

import cn.jfast.framework.base.prop.CoreConsts;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * JFast Api 调用工具
 */
public class ApiCaller {
	
    private static final String GET = "GET";
    private static final String POST = "POST";
    private static final String PUT = "PUT";
    private static final String DELETE = "DELETE";
    private static final String CHARSET = CoreConsts.encoding;
    private static final SSLSocketFactory sslSocketFactory = getSocketFactory();
    private static final SeftHostnameVerifier trustVerifier = new ApiCaller().new SeftHostnameVerifier();

    private class SeftHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    private class SelfTrustManager implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }
    }

    private static SSLSocketFactory getSocketFactory() {
        try {
            TrustManager[] tm = {new ApiCaller().new SelfTrustManager()};
            SSLContext sslContext = SSLContext.getInstance("TLS", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static HttpURLConnection getHttpConnection(String url,
                                                       String method, Map<String, String> headers) throws IOException,
            NoSuchAlgorithmException, NoSuchProviderException,
            KeyManagementException {
        URL _url = new URL(url);
        HttpURLConnection httpUrlConnection = (HttpURLConnection) _url.openConnection();
        if (httpUrlConnection instanceof HttpsURLConnection) {
            ((HttpsURLConnection) httpUrlConnection).setSSLSocketFactory(sslSocketFactory);
            ((HttpsURLConnection) httpUrlConnection).setHostnameVerifier(trustVerifier);
        }
        httpUrlConnection.setRequestMethod(method);
        httpUrlConnection.setDoOutput(true);
        httpUrlConnection.setDoInput(true);
        httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
        if (headers != null && !headers.isEmpty())
            for (Entry<String, String> entry : headers.entrySet())
                httpUrlConnection.setRequestProperty(entry.getKey(), entry.getValue());
        return httpUrlConnection;
    }

    public static String get(String url) {
        return get(url, null, null);
    }

    public static String get(String url, Map<String, String> queryParas) {
        return get(url, queryParas, null);
    }

    public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) {
        return connect(url, queryParas, null, headers, GET);
    }

    public static String post(String url) {
        return post(url, null, null, null);
    }

    public static String post(String url, Map<String, String> queryParas) {
        return post(url, queryParas, null, null);
    }

    public static String post(String url, String data) {
        return post(url, null, data, null);
    }

    public static String post(String url, Map<String, String> queryParas, String data) {
        return post(url, queryParas, data, null);
    }

    public static String post(String url, Map<String, String> queryParas,
    		String data, Map<String, String> headers) {
        return connect(url, queryParas, data, headers, POST);
    }

    public static String put(String url) {
        return put(url, null, null, null);
    }

    public static String put(String url, Map<String, String> queryParas) {
        return put(url, queryParas, null, null);
    }

    public static String put(String url, String data) {
        return put(url, null, data, null);
    }

    public static String put(String url, Map<String, String> queryParas, String data) {
        return put(url, queryParas, data, null);
    }

    public static String put(String url, Map<String, String> queryParas,
    		String data, Map<String, String> headers) {
        return connect(url, queryParas, data, headers, PUT);
    }

    public static String delete(String url) {
        return delete(url, null, null);
    }

    public static String delete(String url, Map<String, String> queryParas) {
        return delete(url, queryParas, null);
    }

    public static String delete(String url, Map<String, String> queryParas, Map<String, String> headers) {
        return connect(url, queryParas, null, headers, DELETE);
    }

    private static String connect(String url, Map<String, String> queryParas,
                                  String data, Map<String, String> headers, String method) {
        HttpURLConnection conn = null;
        try {
            conn = getHttpConnection(buildUrlWithQueryString(url, queryParas),
                    method, headers);
            conn.connect();
            if (null != data) {
                ObjectOutputStream objOut = new ObjectOutputStream(conn.getOutputStream());
                objOut.writeObject(data);
                objOut.flush();
                objOut.close();
            }
            return readResponseString(conn);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    private static String readResponseString(HttpURLConnection conn) {
    	StringBuilder sb = new StringBuilder();
		InputStream inputStream = null;
		try {
			inputStream = conn.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
			String line = null;
			while ((line = reader.readLine()) != null){
				sb.append(line).append("\n");
			}
			return sb.toString();
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
		finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
    }

    public static String buildUrlWithQueryString(String url,
                                                 Map<String, String> queryParas) {
        if (queryParas == null || queryParas.isEmpty())
            return url;
        StringBuilder sb = new StringBuilder(url);
        boolean isFirst;
        if (url.indexOf("?") == -1) {
            isFirst = true;
            sb.append("?");
        } else {
            isFirst = false;
        }
        for (Entry<String, String> entry : queryParas.entrySet()) {
            if (isFirst)
                isFirst = false;
            else
                sb.append("&");
            String key = entry.getKey();
            String value = entry.getValue();
            if (!StringUtils.isEmpty(value))
                try {
                    value = URLEncoder.encode(value, CHARSET);
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            sb.append(key).append("=").append(value);
        }
        return sb.toString();
    }

}
